package com.nulldev.util.crypto.encryptAPI.experimental;

import com.nulldev.util.crypto.encryptAPI.manage.Encryptor;
import com.nulldev.util.math.lNumber;
import com.nulldev.util.other.ByteUtils;
import com.nulldev.util.string.StringsUtil;
import com.nulldev.util.threading.fastES.LtqExecutor;

/**
 * WARNING: This is an experimental encryption method! <br>
 * Use at your OWN risk. <br>
 * How encryption works: <br>
 *  - call calcValue(String) on the password which adds up the char value to an int. <br>
 *  - shifts every byte in a string by calcValue and length. <br>
 * How decryption works: <br>
 * 	- call calcValue(String) on the password which adds up the char value to an int. <br>
 * 	- shifts every byte in a string by calcValue and length in reverse.<br>
 * @author null
 */
public class Shifter extends Encryptor {

	@Override
	public String getEncryptorID() {
		return "experimental.shifter";
	}

	@Override
	public String[] supportedEncryptors() {
		return new String[] {"Shifter"};
	}

	@Override
	public String encrypt(final String text, final String password) {
		return ByteUtils.toBase64(encryptBytes(text.getBytes(), password));
	}
	
	public String encrypt(final String text, final String password, final int offset) {
		return ByteUtils.toBase64(encryptBytes(text.getBytes(), password, offset));
	}

	@Override
	public byte[] encryptBytes(final byte[] data, final String password) {
		final int totalValue = StringsUtil.__calc_shifter(password);
		for (int i = 0; i < data.length; i++) {
			data[i] = (byte) (data[i] + totalValue + password.length());
		}
		return data;
	}
	
	public byte[] encryptBytes(final byte[] data, final String password, final int offset) {
		final int totalValue = StringsUtil.__calc_shifter(password);
		for (int i = 0; i < data.length; i++) {
			data[i] = (byte) (data[i] + totalValue + password.length() + offset);
		}
		return data;
	}

	@Override
	public String decrypt(final String text, final String password) {
		return new String(decryptBytes(ByteUtils.fromBase64(text), password));
	}
	
	public String decrypt(final String text, final String password,final int offset) {
		return new String(decryptBytes(ByteUtils.fromBase64(text), password, offset));
	}

	@Override
	public byte[] decryptBytes(final byte[] data, final String password) {
		final int totalValue = StringsUtil.__calc_shifter(password);
		for (int i = 0; i < data.length; i++) {
			data[i] = (byte) (data[i] - totalValue - password.length());
		}
		return data;
	}
	
	public byte[] decryptBytes(final byte[] data, final String password, final int offset) {
		final int totalValue = StringsUtil.__calc_shifter(password);
		for (int i = 0; i < data.length; i++) {
			data[i] = (byte) (data[i] - totalValue - password.length() - offset);
		}
		return data;
	}
	
	public static void main(String[] args) {
		final Shifter s = new Shifter();
		final String test = s.encrypt("test", "password");
		System.out.println("Result: " + test);
		System.out.println("Result via raw bytes: " + new String(ByteUtils.fromBase64(test)));
		System.out.println("Decrypted: " + s.decrypt(test, "password"));
//		final lNumber attempts = lNumber.nil();
//		final LtqExecutor exec = new LtqExecutor();
//		exec.execute(new Runnable() {
//			
//			@Override
//			public void run() {
//				while (true) {
//					final String testP = StringsUtil.randomString(8);
//					final String attempt = s.decrypt(test, testP);
//					if (attempt.equals(test)) {
//						System.out.println("CRACKED IN " + attempts + " attempts.");
//						System.out.println("Password used: " + testP);
//						break;
//					}else {
//						attempts.addSelf(1L);
//						if (attempts.longValue() % 1000000 == 0) {
//							System.out.println("Attempt number " + attempts + " failed. [result was '" + attempt + "'.]");
//						}
//					}
//				}
//			}
//		});
	}

}
